#include "caller_protocol.h"
#include "infrastructure/utilities/crc.h"
#include <QDebug>
#include <cstdlib>
#include <ctime>

namespace application {
namespace caller {

CallerProtocol::CallerProtocol(std::shared_ptr<kernel::ILogger> logger, QObject* parent)
    : QObject(parent)
    , m_logger(logger)
{
    srand(time(nullptr));
    datetime = QDateTime::currentDateTime();
    m_tcpConnection = new infrastructure::io::TcpSocketClient(this);
    connect(m_tcpConnection, &infrastructure::io::TcpSocketClient::readyRead,
            this, &CallerProtocol::receiveTcpReadyRead);
}

CallerProtocol::CallerProtocol(infrastructure::io::TcpSocketClient* tcpClient,
                               std::shared_ptr<kernel::ILogger> logger,
                               QObject* parent)
    : QObject(parent)
    , m_tcpConnection(tcpClient)
    , m_logger(logger)
{
    datetime = QDateTime::currentDateTime();
    connect(m_tcpConnection, &infrastructure::io::TcpSocketClient::readyRead,
            this, &CallerProtocol::receiveTcpReadyRead);
}

bool CallerProtocol::initTcpConnection(const QHostAddress& address, quint16 port)
{
    if (!m_tcpConnection->initConnection(address, port)) {
        if (m_logger) {
            m_logger->error(QString("呼叫器协议初始化连接失败: %1:%2")
                .arg(address.toString()).arg(port));
        }
        return false;
    }
    return true;
}

qint64 CallerProtocol::getProtocolState() const
{
    return m_tcpConnection->state();
}

void CallerProtocol::setAnalyzeCallbackFunc(protocol_cb_t callback)
{
    m_receiveFinishCallback = callback;
}

bool CallerProtocol::writeTcpData(const QByteArray& sendData, protocol_cb_t callback)
{
    if (!m_tcpConnection->isOpen()) {
        if (m_logger) {
            m_logger->warning("呼叫器协议无连接");
        }
        return false;
    }

    if (callback != nullptr) {
        m_receiveFinishCallback = callback;
    }

    QByteArray head;
    head.append(0xAA);
    head.append(0x55);

    QByteArray data;
    uint16_t dataLen = sendData.size() + 2;
    data.append(dataLen >> 8);
    data.append(dataLen & 0xFF);
    data.append(sendData);

    uint16_t crc = infrastructure::utilities::ModbusCRC::crc16_ccitt_calc_false(
        reinterpret_cast<const uint8_t*>(data.data()), dataLen);
    data.append(crc >> 8);
    data.append(crc & 0xFF);

    head.append(data);

    m_tcpConnection->write(head, head.size());

    if (m_logger) {
        m_logger->debug(QString("呼叫器发送: %1").arg(QString(head.toHex(' '))));
    }

    if (!m_tcpConnection->waitForBytesWritten(m_timeout)) {
        m_lastError = tr("waitForBytesWritten: time out!");
        if (m_logger) {
            m_logger->warning(QString("呼叫器发送超时: %1").arg(m_lastError));
        }
        return false;
    }

    return true;
}

void CallerProtocol::receiveTcpReadyRead()
{
    if (!m_tcpConnection->isOpen() || !m_tcpConnection->isReadable()) {
        return;
    }

    if (m_tcpConnection->state() != QAbstractSocket::ConnectedState) {
        return;
    }

    QByteArray message = m_tcpConnection->read(INT16_MAX);
    message = m_lastMessage + message;
    uint64_t size = message.size();

    while (size > 7) {
        char a0 = message.at(0);
        char a1 = message.at(1);

        if (uint8_t(a0) == 0xAA && uint8_t(a1) == 0x55) {
            if (size >= 7) {
                uint32_t dataSize = 0;
                dataSize = message.mid(2, 2).toHex().toUInt(nullptr, 16);
                int remainingSize = size - 4;

                if (dataSize > remainingSize) {
                    m_lastMessage = message;
                    break;
                } else {
                    QByteArray data = message.mid(2, dataSize);
                    uint16_t calcCrc = infrastructure::utilities::ModbusCRC::crc16_ccitt_calc_false(
                        reinterpret_cast<const uint8_t*>(data.data()), dataSize);
                    uint16_t crc = message.mid(dataSize + 2, 2).toHex().toUInt(nullptr, 16);

                    if (crc == calcCrc) {
                        if (m_receiveFinishCallback != nullptr) {
                            m_receiveFinishCallback(data);
                        }
                    } else if (m_logger) {
                        m_logger->warning("呼叫器协议CRC校验失败");
                    }

                    message = message.right(remainingSize - dataSize);
                    size = message.size();
                    m_lastMessage.clear();
                }
            } else {
                m_lastMessage = message;
                break;
            }
        } else {
            // 报头数据错误，找不到0xAA55就一步步往后挪
            message = message.right(size - 1);
            size = message.size();
        }
    }
}

} // namespace caller
} // namespace application
